import {
  Controller,
  Param,
  Get,
  Post,
  Put,
  Delete,
  Body,
  UseInterceptors,
  Request,
  Patch,
  Inject,
} from "@nestjs/common";
import { LoginInterceptor } from "src/user/interceptor/login.interceptor";
import { PrismaClient } from "@prisma/client";

import { WebResult } from "src/common/web.result";
import { UserRegisterDTO } from "./dto/user-register.dto";
import { UserLoginDTO } from "./dto/user-login.dto";
import { UserTokenService } from "./token.service";
import { UserUpdatePasswordDTO } from "./dto/user-update-password.dto";
import { UserUpdateNicknameDTO } from "./dto/user-update-nickname.dto";
import { UserUpdateEmailDTO } from "./dto/user-update-email.dto";
import { FastifyRequest } from "fastify";
import { Transporter } from "nodemailer";
import configuration from "src/common/configuration";
import { UserService } from "./user.service";

declare module "fastify" {
  interface Session {
    emailCode: string;
    emailCodeLastGetTime: Date;
  }
}

@Controller("user")
export class UserController {
  constructor(
    private readonly prisma: PrismaClient,
    @Inject("Transporter") private readonly emailTrasnporter: Transporter,
    private readonly userService: UserService,
    private readonly userTokenService: UserTokenService,
  ) {}

  @Get("/verifyToken")
  @UseInterceptors(LoginInterceptor)
  verifyToken() {
    return WebResult.success(true);
  }

  @Get("/id/:id")
  async getByUserID(@Param("id") id: string) {
    const uid = parseInt(id);
    if (!uid) {
      return WebResult.failure(undefined, "id is not a number");
    }
    const userDTO: any = await this.prisma.user.findUnique({
      select: {
        id: true,
        nickname: true,
        username: true,
        register_time: true,
        last_login_time: true,
      },
      where: {
        id: uid,
      },
    });
    if (!userDTO) return WebResult.failure();
    userDTO.register_time = `${userDTO.register_time.getFullYear()}/${userDTO.register_time.getMonth() + 1}/${userDTO.register_time.getDate()}`;
    return WebResult.success(userDTO);
  }

  @Get("/username/:username")
  async getByUsername(@Param("username") username: string) {
    const userDTO: any = await this.prisma.user.findUnique({
      select: {
        id: true,
        avatar: true,
        nickname: true,
        username: true,
        register_time: true,
        last_login_time: true,
      },
      where: {
        username,
      },
    });
    if (!userDTO) return WebResult.failure();
    userDTO.register_time = `${userDTO.register_time.getFullYear()}/${userDTO.register_time.getMonth() + 1}/${userDTO.register_time.getDate()}`;
    return WebResult.success(userDTO);
  }

  @Post(["/", "/register"])
  async register(@Body() userRegisterDTO: UserRegisterDTO) {
    let userNickname = userRegisterDTO.nickname;
    if (!userNickname) userNickname = userRegisterDTO.username;
    let user = await this.prisma.user.findUnique({
      select: {
        id: true,
      },
      where: {
        username: userRegisterDTO.username,
      },
    });
    if (user) return WebResult.failure(undefined, "用户名已存在");

    user = await this.prisma.user.findUnique({
      select: {
        id: true,
      },
      where: {
        nickname: userNickname,
      },
    });
    if (user) return WebResult.failure(undefined, "昵称已存在");

    try {
      await this.prisma.user.create({
        data: {
          nickname: userNickname,
          username: userRegisterDTO.username,
          password: userRegisterDTO.password,
        },
      });
      return WebResult.success(undefined, "注册成功");
    } catch (e) {
      console.error(e);
      return WebResult.failure(undefined, "注册失败");
    }
  }

  @Post("/login")
  async login(@Body() userLoginDTO: UserLoginDTO) {
    const user = await this.prisma.user.findUnique({
      select: {
        id: true,
        password: true,
      },
      where: {
        username: userLoginDTO.username,
      },
    });
    if (!user) return WebResult.failure(undefined, "用户不存在");

    if (userLoginDTO.password !== user.password)
      return WebResult.failure(undefined, "密码错误");
    const token = this.userTokenService.generateToken({
      userID: user.id,
      username: userLoginDTO.username,
      password: user.password,
    });
    return WebResult.success(token, "登录成功");
  }

  @Put(["/", "/update"])
  @UseInterceptors(LoginInterceptor)
  update() {
    return WebResult.failure(undefined, "暂未开发");
  }

  @Patch("/nickname")
  @UseInterceptors(LoginInterceptor)
  async updateNickname(
    @Request() request: any,
    @Body() userUpdateNicknameDTO: UserUpdateNicknameDTO,
  ) {
    const uid = this.userTokenService.getUserIDFromRequest(request);
    try {
      await this.prisma.user.update({
        where: {
          id: uid,
        },
        data: {
          nickname: userUpdateNicknameDTO.nickname,
        },
      });
      return WebResult.success(undefined, "修改成功");
    } catch (e) {
      console.error(e);
      return WebResult.failure(undefined, "修改失败");
    }
  }

  @Patch("/password")
  @UseInterceptors(LoginInterceptor)
  async updatePassword(
    @Request() request: any,
    @Body() userUpdatePasswordDTO: UserUpdatePasswordDTO,
  ) {
    const uid = this.userTokenService.getUserIDFromRequest(request);
    const user = await this.prisma.user.findUnique({
      where: {
        id: uid,
      },
    });
    if (!user) {
      return WebResult.failure(undefined, "用户不存在");
    }
    if (userUpdatePasswordDTO.password !== user.password) {
      return WebResult.failure(undefined, "旧密码错误");
    }

    try {
      await this.prisma.user.update({
        where: {
          id: uid,
        },
        data: {
          password: userUpdatePasswordDTO.new_password,
        },
      });
      return WebResult.success(undefined, "修改成功");
    } catch (e) {
      console.error(e);
      return WebResult.failure(undefined, "修改失败");
    }
  }

  @Post("/email/code")
  @UseInterceptors(LoginInterceptor)
  async getUpdateEmailCode(
    @Request() request: FastifyRequest,
    @Body() userUpdateEmailDTO: UserUpdateEmailDTO,
  ) {
    if (!configuration.email.enable) {
      return WebResult.failure(undefined, "邮箱功能未开启");
    }

    const nowTime = new Date();
    if (
      request.session.emailCodeLastGetTime &&
      nowTime.getTime() - request.session.emailCodeLastGetTime.getTime() <
        60 * 1000
    ) {
      return WebResult.failure(undefined, "请稍后再试");
    }

    const code = this.userService.generateCode();
    request.session.emailCode = code;
    request.session.emailCodeLastGetTime = nowTime;
    try {
      this.emailTrasnporter.sendMail({
        from: `"${configuration.website.name}" <${configuration.email.user}>`,
        to: userUpdateEmailDTO.email,
        subject: `来自${configuration.website.name}的验证码`,
        text: `您的验证码是：${code}`,
      });
      return WebResult.success(undefined, "验证码已发送");
    } catch (e) {
      console.error(e);
      return WebResult.failure(undefined, "内部错误，验证码获取失败");
    }
  }

  @Patch("/email")
  @UseInterceptors(LoginInterceptor)
  async updateEmail(
    @Request() request: FastifyRequest,
    @Body() userUpdateEmailDTO: UserUpdateEmailDTO,
  ) {
    if (
      !request.session.emailCode ||
      userUpdateEmailDTO.code !== request.session.emailCode
    ) {
      return WebResult.failure(undefined, "验证码错误");
    }

    const uid = this.userTokenService.getUserIDFromRequest(request);
    try {
      await this.prisma.user.update({
        where: {
          id: uid,
        },
        data: {
          email: userUpdateEmailDTO.email,
        },
      });
      return WebResult.success(undefined, "修改成功");
    } catch (e) {
      console.error(e);
      return WebResult.failure(undefined, "修改失败");
    }
  }

  @Delete(["/", "/delete"])
  @UseInterceptors(LoginInterceptor)
  delete() {
    return WebResult.failure(undefined, "暂未开发");
  }
}
